﻿#include "HnrjitControl.h"
#include "HnrjitContainer.h"
#include "HnrjitDialog.h"
#include "HnrjitImaging.h"

CHnrjitControl::CHnrjitControl()
	: CHnrjitWigdet()
	, m_nTextAlign(DT_TOP | DT_LEFT)
	, m_tagFont(GetStockObject(SYSTEM_FONT))
	, m_isTriggered(FALSE)
	, m_pContainer(NULL)
{
	m_crBackColors[kNormal] = m_crBackColors[kActivated] = m_crBackColors[kDisabled] = CLR_NONE;
	m_crForeColors[kNormal] = m_crForeColors[kActivated] = m_crForeColors[kDisabled] = RGB(0xff, 0xff, 0xff);
	m_3dBorderColors[kNormal] = m_3dBorderColors[kActivated] = m_3dBorderColors[kDisabled] = CLR_NONE;
}

CHnrjitControl::~CHnrjitControl()
{
	ATLASSERT(m_dwThreadID == GetCurrentThreadId());
	if (m_tagFont) DeleteObject(m_tagFont);
	if (m_pContainer) m_pContainer->RemoveControl(this);
}

void CHnrjitControl::SetAttribute(const TiXmlElement* pxElement, HZIP hZipRes /*= NULL*/)
{
	CHnrjitWigdet::SetAttribute(pxElement, hZipRes);

	const CHAR* pszValue;
	if (pszValue = pxElement->Attribute("Title"))
	{
		CAtlArray<TCHAR> caResult;
		const TCHAR* pszTitle = CHnrjitImaging::Utf8ToLocal(pszValue, &caResult);
		if (pszTitle) SetTitle(pszTitle);
	}
	if (pszValue = pxElement->Attribute("TextAlign"))
	{ // 参考DrawText中的uFormat参数说明
		DWORD dwTextAlign = strtol(pszValue, NULL, 10);
		SetTextAlign(dwTextAlign);
	}
	SetXxxColor fnSetXxxColors[3] = { 0 };
	const CHAR* pszXxxValues[3] = { 0 };
	if (pszValue = pxElement->Attribute("BackColors"))
	{
		fnSetXxxColors[CHnrjitControl::kNormal] = &CHnrjitControl::SetBackColor;
		pszXxxValues[CHnrjitControl::kNormal] = pszValue;
	}
	if (pszValue = pxElement->Attribute("ForeColors"))
	{
		fnSetXxxColors[CHnrjitControl::kActivated] = &CHnrjitControl::SetForeColor;
		pszXxxValues[CHnrjitControl::kActivated] = pszValue;
	}
	if (pszValue = pxElement->Attribute("BorderColors"))
	{
		fnSetXxxColors[CHnrjitControl::kDisabled] = &CHnrjitControl::SetBorderColor;
		pszXxxValues[CHnrjitControl::kDisabled] = pszValue;
	}
	for (size_t i = 0; i < _countof(fnSetXxxColors); i++)
	{
		SetXxxColor fnSetXxxColor = fnSetXxxColors[i];
		const CHAR* pszXxxValue = pszXxxValues[i];

		if (fnSetXxxColors && pszXxxValue)
		{
			CHAR* pszEndPtr = NULL;

			const CHAR* pszColor = (pszXxxValue[0] == '#') ? pszXxxValue + 1 : pszXxxValue;
			LONG lColor = strtol(pszColor, &pszEndPtr, 16);
			(this->*fnSetXxxColor)(CHnrjitControl::kNormal, lColor);

			if (pszEndPtr)
			{
				pszColor = (pszEndPtr[0] == '#') ? pszEndPtr + 1 : pszEndPtr;
				lColor = strtol(pszEndPtr + 1, &pszEndPtr, 16);
				(this->*fnSetXxxColor)(CHnrjitControl::kActivated, lColor);

				if (pszEndPtr)
				{
					pszColor = (pszEndPtr[0] == '#') ? pszEndPtr + 1 : pszEndPtr;
					lColor = strtol(pszEndPtr + 1, &pszEndPtr, 16);
					(this->*fnSetXxxColor)(CHnrjitControl::kDisabled, lColor);
				}
			}
			else
			{
				(this->*fnSetXxxColor)(CHnrjitControl::kActivated, lColor);
				(this->*fnSetXxxColor)(CHnrjitControl::kDisabled, lColor);
			}
		}
	}

	if (pszValue = pxElement->Attribute("BackIcons"))
	{
		CAtlArray<TCHAR> caResult;
		const TCHAR* pszBackIcon = CHnrjitImaging::Utf8ToLocal(pszValue, &caResult);
		if (pszBackIcon)
		{
			CAtlString strBackIcon = pszBackIcon;
			int nCurPos = 0;
			CAtlString strToken = strBackIcon.Tokenize(_T("|"), nCurPos);
			if (strToken.GetLength())
			{
				if (strToken != _T("*"))
				{
					SetBackIcon(CHnrjitControl::kNormal, CHnrjitImaging::LoadFromFile(strToken, hZipRes));
				}

				strToken = strBackIcon.Tokenize(_T("|"), nCurPos);
				if (strToken.GetLength())
				{
					if (strToken != _T("*"))
					{
						SetBackIcon(CHnrjitControl::kActivated, CHnrjitImaging::LoadFromFile(strToken, hZipRes));
					}

					strToken = strBackIcon.Tokenize(_T("|"), nCurPos);
					if (strToken.GetLength())
					{
						if (strToken != _T("*"))
						{	
							SetBackIcon(CHnrjitControl::kDisabled, CHnrjitImaging::LoadFromFile(strToken, hZipRes));
						}
					}
				}
			}
		}
	}

	const TiXmlElement* pxFont = pxElement->FirstChildElement("Font");
	if (pxFont)
	{
		int nFontSize = 8;
		const TCHAR* pszFontName = _T("");
		int nWeight = FW_NORMAL;
		BOOL bItalic = FALSE;
		BOOL bUnderline = FALSE;
		BOOL bStrikeOut = FALSE;
		CAtlArray<TCHAR> caResult;
		if (pszValue = pxFont->Attribute("Size"))
		{
			nFontSize = strtol(pszValue, NULL, 10);
		}
		if (pszValue = pxFont->Attribute("Name"))
		{
			 pszFontName = CHnrjitImaging::Utf8ToLocal(pszValue, &caResult);
		}
		if (pszValue = pxFont->Attribute("Bold"))
		{
			if (strcmp(pszValue, "true") == 0)
			{
				nWeight = FW_BOLD;
			}
		}
		if (pszValue = pxFont->Attribute("Italic"))
		{
			if (strcmp(pszValue, "true") == 0)
			{
				bItalic = TRUE;
			}
		}
		if (pszValue = pxFont->Attribute("Underline"))
		{
			if (strcmp(pszValue, "true") == 0)
			{
				bUnderline = TRUE;
			}
		}
		if (pszValue = pxFont->Attribute("StrikeOut"))
		{
			if (strcmp(pszValue, "true") == 0)
			{
				bStrikeOut = TRUE;
			}
		}
		SetFont(nFontSize, pszFontName, nWeight, bItalic, bUnderline, bStrikeOut);
	}
}

CHnrjitControl::ControlStatus CHnrjitControl::GetControlState() const
{
	return (m_isEnabled && m_pContainer->IsEnabled()) ? (m_isTriggered ? kActivated : kNormal) : kDisabled;
}

const CAtlString& CHnrjitControl::GetTitle() const
{
	return m_strTitle;
}

void CHnrjitControl::SetTitle(const TCHAR* pszTitle)
{
	ATLASSERT(m_dwThreadID == GetCurrentThreadId());
	if (m_strTitle != pszTitle)
	{
		m_strTitle = pszTitle;
		Invalidate(m_rcGeometry);
	}
}

UINT CHnrjitControl::GetTextAlign() const
{
	return m_nTextAlign;
}

void CHnrjitControl::SetTextAlign(UINT nTextAlign)
{
	ATLASSERT(m_dwThreadID == GetCurrentThreadId());
	if (m_nTextAlign != nTextAlign)
	{
		m_nTextAlign = nTextAlign;
		Invalidate(m_rcGeometry);
	}
}

BOOL CHnrjitControl::GetFont(LOGFONT* pLogFont)
{
	return GetObject(m_tagFont, sizeof(LOGFONT), pLogFont);
}

void CHnrjitControl::SetFont(LOGFONT* pLogFont)
{
	ATLASSERT(m_dwThreadID == GetCurrentThreadId());
	if (m_tagFont) DeleteObject(m_tagFont);
	m_tagFont = pLogFont ? CreateFontIndirect(pLogFont) : GetStockObject(SYSTEM_FONT);
	Invalidate(m_rcGeometry);
}

void CHnrjitControl::SetFont(int nFontSize, const TCHAR* pszFontName, int nWeight /*= FW_NORMAL*/, BYTE bItalic /*= FALSE*/, BYTE bUnderline /*= FALSE*/, BYTE bStrikeOut /*= FALSE*/, HDC hDC /*= NULL*/)
{
	ATLASSERT(m_dwThreadID == GetCurrentThreadId());
	if (m_tagFont) DeleteObject(m_tagFont);

	LONG lfHeight = (LONG) (((__int64) nFontSize) * ((__int64) GetDeviceCaps(hDC, LOGPIXELSY)) / 72LL);
	m_tagFont = CreateFont(lfHeight, 0, 0, 0, nWeight, bItalic, bUnderline, bStrikeOut, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, pszFontName);
	Invalidate(m_rcGeometry);
}

COLORREF CHnrjitControl::GetBackColor(ControlStatus iControlStatus) const
{
	ATLASSERT(iControlStatus == kNormal || iControlStatus == kActivated || iControlStatus == kDisabled);
	return m_crBackColors[iControlStatus];
}

void CHnrjitControl::SetBackColor(ControlStatus iControlStatus, COLORREF crBackColor)
{
	ATLASSERT(m_dwThreadID == GetCurrentThreadId());
	ATLASSERT(iControlStatus == kNormal || iControlStatus == kActivated || iControlStatus == kDisabled);
	if (m_crBackColors[iControlStatus] != crBackColor)
	{
		m_crBackColors[iControlStatus] = crBackColor;
		Invalidate(m_rcGeometry);
	}
}

void CHnrjitControl::SetBackColor(COLORREF crBackColor)
{
	ATLASSERT(m_dwThreadID == GetCurrentThreadId());
	if (m_crBackColors[kDisabled] != crBackColor || m_crBackColors[kNormal] != crBackColor || m_crBackColors[kActivated] != crBackColor)
	{
		m_crBackColors[kDisabled] = m_crBackColors[kNormal] = m_crBackColors[kActivated] = crBackColor;
		Invalidate(m_rcGeometry);
	}
}

COLORREF CHnrjitControl::GetForeColor(ControlStatus iControlStatus) const
{
	ATLASSERT(iControlStatus == kNormal || iControlStatus == kActivated || iControlStatus == kDisabled);
	return m_crForeColors[iControlStatus];
}

void CHnrjitControl::SetForeColor(ControlStatus iControlStatus, COLORREF crForeColor)
{
	ATLASSERT(m_dwThreadID == GetCurrentThreadId());
	ATLASSERT(iControlStatus == kNormal || iControlStatus == kActivated || iControlStatus == kDisabled);
	if (m_crForeColors[iControlStatus] != crForeColor)
	{
		m_crForeColors[iControlStatus] = crForeColor;
		Invalidate(m_rcGeometry);
	}
}

void CHnrjitControl::SetForeColor(COLORREF crForeColor)
{
	ATLASSERT(m_dwThreadID == GetCurrentThreadId());
	if (m_crForeColors[kDisabled] != crForeColor || m_crForeColors[kNormal] != crForeColor || m_crForeColors[kActivated] != crForeColor)
	{
		m_crForeColors[kDisabled] = m_crForeColors[kNormal] = m_crForeColors[kActivated] = crForeColor;
		Invalidate(m_rcGeometry);
	}
}

COLORREF CHnrjitControl::GetBorderColor(ControlStatus iControlStatus) const
{
	ATLASSERT(iControlStatus == kNormal || iControlStatus == kActivated || iControlStatus == kDisabled);
	return m_3dBorderColors[iControlStatus];
}

void CHnrjitControl::SetBorderColor(ControlStatus iControlStatus, COLORREF crBorderColor)
{
	ATLASSERT(m_dwThreadID == GetCurrentThreadId());
	ATLASSERT(iControlStatus == kNormal || iControlStatus == kActivated || iControlStatus == kDisabled);
	if (m_3dBorderColors[iControlStatus] != crBorderColor)
	{
		m_3dBorderColors[iControlStatus] = crBorderColor;
		Invalidate(m_rcGeometry);
	}
}

void CHnrjitControl::SetBorderColor(COLORREF crBorderColor)
{
	ATLASSERT(m_dwThreadID == GetCurrentThreadId());
	if (m_3dBorderColors[kDisabled] != crBorderColor || m_3dBorderColors[kNormal] != crBorderColor || m_3dBorderColors[kActivated] != crBorderColor)
	{
		m_3dBorderColors[kDisabled] = m_3dBorderColors[kNormal] = m_3dBorderColors[kActivated] = crBorderColor;
		Invalidate(m_rcGeometry);
	}
}

CHnrjitContainer* CHnrjitControl::GetContainer() const
{
	return m_pContainer;
}

void CHnrjitControl::SetContainer(CHnrjitContainer* pContainer)
{
	if (m_pContainer)
	{
		// 只允许被一个容器承载。
		m_pContainer->RemoveControl(this);
	}
	if (pContainer)
	{
		pContainer->AddControl(this);
	}
}

CComPtr<IImage> CHnrjitControl::GetBackIcon(ControlStatus iControlStatus) const
{
	ATLASSERT(iControlStatus == kNormal || iControlStatus == kActivated || iControlStatus == kDisabled);
	return m_spBackIcons[iControlStatus];
}

void CHnrjitControl::SetBackIcon(ControlStatus iControlStatus, CComPtr<IImage> imgPicture)
{
	ATLASSERT(m_dwThreadID == GetCurrentThreadId());
	ATLASSERT(iControlStatus == kNormal || iControlStatus == kActivated || iControlStatus == kDisabled);
	if (m_spBackIcons[iControlStatus] != imgPicture)
	{
		m_spBackIcons[iControlStatus] = imgPicture;
		Invalidate(m_rcGeometry);
	}
}

void CHnrjitControl::AutoSize(ControlStatus iControlStatus)
{
	ATLASSERT(iControlStatus == kNormal || iControlStatus == kActivated || iControlStatus == kDisabled);
	if (m_spBackIcons[iControlStatus])
	{
		ImageInfo ImgInfo;
		if (m_spBackIcons[iControlStatus]->GetImageInfo(&ImgInfo) == S_OK)
		{
			SetSize(CSize(ImgInfo.Width, ImgInfo.Height));
		}
	}
}

void CHnrjitControl::OnClicked()
{
	m_pContainer->OnClicked(this);
}

void CHnrjitControl::OnMousePress(UINT nFlags, CPoint point)
{
	ATLASSERT(m_dwThreadID == GetCurrentThreadId());
	if (!m_isTriggered)
	{
		m_isTriggered = TRUE;
		Invalidate(m_rcGeometry);
	}
}

void CHnrjitControl::OnMouseRelease(UINT nFlags, CPoint point)
{
	ATLASSERT(m_dwThreadID == GetCurrentThreadId());
	if (m_isTriggered)
	{
		m_isTriggered = FALSE;
		Invalidate(m_rcGeometry);
		OnClicked();
	}
}

void CHnrjitControl::Invalidate(const CRect& rcRect)
{
	if (m_pContainer)
	{
		CPoint ptLocation = GetLocation() + m_pContainer->GetLocation();
		m_pContainer->Invalidate(CRect(ptLocation, rcRect.Size()));
	}
}

void CHnrjitControl::DrawTitle(HDC hdc, const CRect& rcGeometry, ControlStatus iControlStatus)
{
	ATLASSERT(iControlStatus == kNormal || iControlStatus == kActivated || iControlStatus == kDisabled);
	if (m_strTitle.GetLength())
	{
		HGDIOBJ hOldFont = SelectObject(hdc, m_tagFont);
		SetTextColor(hdc, m_crForeColors[iControlStatus]);
		if ((m_nTextAlign & DT_VCENTER) && (m_nTextAlign & DT_WORDBREAK))
		{
			CRect rcControl = rcGeometry;
			CRect rcVCenter = rcControl;
			int dwTextHeight = DrawText(hdc, m_strTitle, -1, rcVCenter, (m_nTextAlign & ~DT_VCENTER) | DT_CALCRECT); // 获得文本高度
			rcControl.DeflateRect(0, (rcControl.Height() - dwTextHeight) / 2); // 改变矩形高度

			DrawText(hdc, m_strTitle, -1, rcControl, m_nTextAlign & ~DT_VCENTER);
		}
		else
		{
			DrawText(hdc, m_strTitle, -1, const_cast<CRect& >(rcGeometry), m_nTextAlign);
		}
		SelectObject(hdc, hOldFont);
	}
}

void CHnrjitControl::DrawRect(HDC hdc, const CRect& rcGeometry, ControlStatus iControlStatus)
{
	ATLASSERT(iControlStatus == kNormal || iControlStatus == kActivated || iControlStatus == kDisabled);
	if (!m_isTransparent && !m_pContainer->IsTransparent())
	{
		if (m_crBackColors[iControlStatus] != CLR_NONE)
		{
			SetBkColor(hdc, m_crBackColors[iControlStatus]);
			ExtTextOut(hdc, 0, 0, ETO_OPAQUE, rcGeometry, NULL, 0, NULL);
		}
		if (m_3dBorderColors[iControlStatus] != CLR_NONE)
		{
			int x = rcGeometry.left;
			int y = rcGeometry.top;
			int cx = rcGeometry.right - x;
			int cy = rcGeometry.bottom - y;

			SetBkColor(hdc, m_3dBorderColors[iControlStatus]);
			ExtTextOut(hdc, 0, 0, ETO_OPAQUE, CRect(x, y, x + (cx - 1), y + 1), NULL, 0, NULL);

			SetBkColor(hdc, m_3dBorderColors[iControlStatus]);
			ExtTextOut(hdc, 0, 0, ETO_OPAQUE, CRect(x, y, x + 1, y + (cy - 1)), NULL, 0, NULL);

			SetBkColor(hdc, m_3dBorderColors[iControlStatus]);
			ExtTextOut(hdc, 0, 0, ETO_OPAQUE, CRect((x + cx), y, (x + cx) - 1, y + cy), NULL, 0, NULL);

			SetBkColor(hdc, m_3dBorderColors[iControlStatus]);
			ExtTextOut(hdc, 0, 0, ETO_OPAQUE, CRect(x, (y + cy), x + cx, (y + cy) - 1), NULL, 0, NULL);
		}
	}
}

void CHnrjitControl::DrawImage(HDC hdc, const CRect& rcGeometry, ControlStatus iControlStatus)
{
	ATLASSERT(m_spBackIcons[iControlStatus] != NULL);
	ATLASSERT(iControlStatus == kNormal || iControlStatus == kActivated || iControlStatus == kDisabled);

	if (!m_isTransparent && !m_pContainer->IsTransparent())
	{
		m_spBackIcons[iControlStatus]->Draw(hdc, rcGeometry, NULL);
	}
}
